﻿//-----------------------------------------------------------------------
// <copyright file="ItemTagger.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation.
// All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
// </copyright>
// <author>Yogesh M Joshi</author>
// <email>ymjoshi@live.com</email>
// <date>16-May-2011</date>
//-----------------------------------------------------------------------

namespace SmartTag.Application.Tagging
{
    using System.Collections.Generic;
    using System.IO;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Taxonomy;
    using SmartTag.Common.IFilter;
    using SmartTag.Common.Taxonomy;

    /// <summary>
    /// Item tagger is used to tag specific document item
    /// </summary>
    internal class ItemTagger
    {
        #region "Private Fields"

        /// <summary>
        /// Object used to synchronize operations on item
        /// </summary>
        private static object lockObject = new object();

        /// <summary>
        /// Item to tag
        /// </summary>
        private SPListItem item;

        /// <summary>
        /// Terms are pre-fetched per column basis in termsRepository
        /// </summary>
        private Dictionary<string, List<Term>> termsRepository = new Dictionary<string, List<Term>>();

        /// <summary>
        /// Fields are pre-identified before we start worker threads
        /// </summary>
        private Dictionary<string, SPField> fieldRepository = new Dictionary<string, SPField>();

        /// <summary>
        /// Number of words to scan from document
        /// </summary>
        private int wordsToScan;

        /// <summary>
        /// Relevance of the word to be identified for tagging
        /// </summary>
        private int relevance;

        #endregion

        /// <summary>
        /// Initializes a new instance of the ItemTagger class
        /// </summary>
        /// <param name="item">Item to be tagged</param>
        /// <param name="fieldRepository">Fields to be updated</param>
        /// <param name="termsRepository">Terms to be used</param>
        /// <param name="wordsToScan">Number of words to scan from document</param>
        /// <param name="relevance">Relevance of the term</param>
        public ItemTagger(
            SPListItem item,
            Dictionary<string, SPField> fieldRepository,
            Dictionary<string, List<Term>> termsRepository,
            int wordsToScan,
            int relevance)
        {
            this.item = item;
            this.fieldRepository = fieldRepository;
            this.termsRepository = termsRepository;
            this.wordsToScan = wordsToScan;
            this.relevance = relevance;
        }

        /// <summary>
        /// Tag the item
        /// </summary>
        public void Tag()
        {
            string contents = string.Empty;

            // Check if item has a file
            if (this.item.File != null)
            {
                using (FilterWrapper filter = new FilterWrapper(Path.GetExtension(this.item.File.Name)))
                {
                    // One operation at a time !!!
                    lock (Tagger.LockObject)
                    {
                        using (Stream stream = this.item.File.OpenBinaryStream())
                        {
                            filter.InitializeFromStream(stream);
                            using (FilterStreamReader reader = new FilterStreamReader(filter))
                            {
                                contents = reader.ReadWords(this.wordsToScan);
                            }
                        }
                    }
                }
            }

            // We are safe now
            if (string.IsNullOrEmpty(contents) == false)
            {
                TaxonomyField taxField = null;
                List<Term> terms = null;

                // Tag all of the taxonomy fields
                foreach (SPField field in this.fieldRepository.Values)
                {
                    taxField = field as TaxonomyField;
                    if (taxField != null)
                    {
                        terms = this.termsRepository[taxField.Id.ToString()];
                        Tagger.Tag(this.item, terms, taxField, contents, this.relevance);
                    }
                }

                // We can not call set value and update at same time on different items
                lock (Tagger.LockObject)
                {
                    if (taxField != null)
                    {
                        this.item.SystemUpdate(false);
                    }
                }
            }
        }
    }
}
